<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="" rel="stylesheet">
<style>
    body {
  margin:0;
  background-color:#000;
  color:#fff;
  font-family: 'Raleway', sans-serif;
}

canvas {
  position:absolute;
  top:0;
  left:0;
  width:100%;
  height:100%;
}

.intro {
  position:absolute;
  z-index:2;
  top:25px;
  left:25%;
  width:50%;
  text-align:center;
  pointer-events:none;
}
h1 {
  margin:10px 0;
  text-shadow:0 0 20px rgba(255,255,255,.5);
  font-family: 'Berkshire Swash', cursive;
}
p {
  color:rgba(255,255,255,.75);
}
</style>
</head>
<body>
  
<div class="intro">
  <h1>鼠标点击滑动</h1>
  <p>Paint with your cursor to create a galaxy</p>
</div>
</body>
</html>
<script type="text/javascript">
    var w = window.innerWidth;
var h = window.innerHeight;

var dustCanvas = document.createElement('canvas');
var dustCtx = dustCanvas.getContext('2d');
var starCanvas = document.createElement('canvas');
var starCtx = starCanvas.getContext('2d');

document.body.appendChild(dustCanvas);
document.body.appendChild(starCanvas);

dustCanvas.width = starCanvas.width = w;
dustCanvas.height = starCanvas.height = h;

dustCtx.globalCompositeOperation = 'lighter';
starCtx.globalCompositeOperation = 'lighter';

var galaxies = [];

var mouse = {
  pos: {
    x: w * 0.5,
    y: h * 0.5
  },
  speed: 0
};

var randomNumbers = length => Array.from(new Array(length), () => Math.random());
var PI = Math.PI;
var TAU = PI * 2;
var r = () => Math.random();
var angle2 = (p1,p2) => Math.atan2(
  p2[1]-p1[1],
  p2[0]-p1[0]
);
var distance2 = (p1,p2) => Math.sqrt(
  Math.pow(p1[0]-p2[0], 2) + 
  Math.pow(p1[1]-p2[1], 2)
);

var createDustParticle = (color) => {

  var canvas = document.createElement('canvas');

  var w = 100;
  var h = 100;
  var cx = w * 0.5;
  var cy = h * 0.5;

  canvas.width = w;
  canvas.height = h;
  var ctx = canvas.getContext('2d');
  canvas.ctx = ctx;

  var xRand = -5 + (r()*10);
  var yRand = -5 + (r()*10);
  var xRand2 = 10 + (r()*(cx/2));
  var yRand2 = 10 + (r()*(cy/2));

  var color = color || {
    r: Math.round(150+(r()*100)),
    g: Math.round(50+(r()*100)),
    b: Math.round(50+(r()*100))
  };

  ctx.fillStyle = `rgba(${color.r},${color.g},${color.b},.02)`;

  Array
    .from(new Array(200), () => randomNumbers(3))
    .forEach( (p,i,arr) => {
      var length = arr.length;

      var x = Math.cos( TAU/xRand/length*i ) * p[2]*xRand2 + cx;
      var y = Math.sin( TAU/yRand/length*i ) * p[2]*yRand2 + cy;

      ctx.beginPath();
      ctx.moveTo(x, y);
      ctx.arc(x, y, p[2]*4, 0, TAU);
      ctx.fill();

    });

    return canvas;
};

var Galaxy = function(x,y) {

  var g = this;

  g.x = x;
  g.y = y;
  g.stars = [];
  g.dust = [];
  g.drag = r();

  g.angleOffsetX = TAU*r();
  g.angleOffsetY = TAU*r();
  g.realAngleOffsetX = 0;
  g.realAngleOffsetY = 0;

  g.color = {
    r: Math.round(50+(r()*100)),
    g: Math.round(50+(r()*100)),
    b: Math.round(150+(r()*100))
  };

  var calculateStarDustParams = o => {
    o.angle = angle2([g.x,g.y], [o.x,o.y]);
    o.distance = distance2([g.x,g.y], [o.x,o.y]);
    o.xAspect = [o.x/o.y];
    o.yAspect = [o.y/o.x];
  };

  g.calculateCenter = () => {
    if (!g.stars.length) return;
    g.x = g.stars
      .map(s => s.x)
      .reduce((previous,current) => previous + current)
      / g.stars.length;

    g.y = g.stars
      .map(s => s.y)
      .reduce((previous,current) => previous + current)
      / g.stars.length;

    g.stars.forEach(calculateStarDustParams);
    g.dust.forEach(calculateStarDustParams);
  };

};

var Star = function(x, y, spread) {
  var s = this;
  s.x = x + Math.cos(TAU*r()) * spread;
  s.y = y + Math.sin(TAU*r()) * spread;
  s.radius = r()+0.25;
  s.speed = r();
};

var Dust = function(x, y, size) {
  var d = this;
  d.x = x;
  d.y = y;
  d.size = size;
  d.texture = createDustParticle();
  d.speed = r();
};

var updateStarDust = (s,g) => {
  if (g == currentGalaxy && drawingMode) return;
  s.angle += (0.5+(s.speed*0.5))/s.distance;
  s.x = g.x + (Math.cos(s.angle+g.realAngleOffsetX)*s.distance);
  s.y = g.y + (Math.sin(s.angle+g.realAngleOffsetY)*s.distance);
};

var update = () => {
  galaxies.forEach(g => {
    if (g != currentGalaxy) {
      g.realAngleOffsetX +=
        g.realAngleOffsetX < g.angleOffsetX
        ? (g.angleOffsetX-g.realAngleOffsetX)*0.05 : 0;
      g.realAngleOffsetY +=
        g.realAngleOffsetY < g.angleOffsetY
        ? (g.angleOffsetY-g.realAngleOffsetY)*0.05 : 0;
    }
    g.stars.forEach((s) => {
      /*s.distance -= s.distance < 2
        ? 0
        : TAU/s.distance;*/
      updateStarDust(s,g);
    });
    g.dust.forEach((d) => {
      /*d.distance -= d.distance < 50
        ? 0
        : TAU/d.distance;*/
      updateStarDust(d,g);
    });
  });
};

var render = () => {

  dustCtx.globalCompositeOperation = 'source-over';
  dustCtx.fillStyle = 'rgba(0,0,0,.05)';
  dustCtx.fillRect(0,0,w,h);
  dustCtx.globalCompositeOperation = 'lighter';

  starCtx.clearRect(0,0,w,h);
  starCtx.fillStyle = '#ffffff';
  starCtx.strokeStyle = 'rgba(255,255,255,.05)';
  starCtx.beginPath();

  if (drawingMode) galaxies.forEach(g => {
    starCtx.moveTo(g.x, g.y);
    starCtx.arc(g.x,g.y,2,0,TAU);
  });

  galaxies.forEach(g => {
    g.stars.forEach(s => {
      starCtx.moveTo(s.x, s.y);
      starCtx.arc(s.x,s.y,s.radius,0,TAU);
    });
    g.dust.forEach(d => {
      dustCtx.drawImage(d.texture, d.x-(d.size*0.5), d.y-(d.size*0.5), d.size, d.size)
    });
  });

  dustCtx.fill();
  starCtx.fill();

  if (drawingMode && currentGalaxy) {
    starCtx.beginPath();
    currentGalaxy.stars.forEach((s,i) => {
        starCtx.moveTo(s.x, s.y);
        starCtx.lineTo(currentGalaxy.x, currentGalaxy.y);
    });
    starCtx.stroke();
  }

};

var currentGalaxy = null;

var drawingMode = false;

var activateDraw = e => {
  drawingMode = true;
  mouse.pos.x = e.layerX;
  mouse.pos.y = e.layerY;
  currentGalaxy = new Galaxy(e.layerX, e.layerY);
  galaxies.push(currentGalaxy);
};
var disableDraw = e => {

  drawingMode = false;
  currentGalaxy = null;
};
var draw = e => {
  if (!drawingMode) return;

  currentGalaxy.stars.push(new Star(mouse.pos.x, mouse.pos.y, mouse.speed));
  currentGalaxy.stars.push(new Star(mouse.pos.x, mouse.pos.y, mouse.speed));
  currentGalaxy.stars.push(new Star(mouse.pos.x, mouse.pos.y, mouse.speed));

  if (mouse.speed*1.5 > 13 && mouse.speed < 100) currentGalaxy.dust.push(
    new Dust(
      (currentGalaxy.x + (Math.cos(TAU*r()) * mouse.speed*0.1)),
      (currentGalaxy.y + (Math.sin(TAU*r()) * mouse.speed*0.1)),
      mouse.speed*1.5
    )
  );

  currentGalaxy.calculateCenter();

};

var loop = () => {
  draw();
  update();
  render();
  window.requestAnimationFrame(loop);
};

loop();

var moveEvent = e => {
  mouse.speed =  distance2(
    [e.layerX,e.layerY],
    [mouse.pos.x,mouse.pos.y]
  );
  mouse.pos.x = e.layerX;
  mouse.pos.y = e.layerY;
  draw(e);
};

window.addEventListener('mousedown', activateDraw);
window.addEventListener('mousemove', moveEvent);
window.addEventListener('mouseup',disableDraw);

window.addEventListener('touchstart', activateDraw);
window.addEventListener('touchmove', moveEvent);
window.addEventListener('touchend',disableDraw);
</script>